/**************************************************************/
/*  This file is licensed from, and is a trade secret of:     */
/*                                                            */
/*    Mercury Computer Systems, Incorporated                  */
/*    199 Riverneck Road                                      */
/*    Chelmsford, Massachusetts 01824-2820                    */
/*    United States of America                                */
/*    Telephone + 1 978 256-1300                              */
/*                                                            */
/*  Refer to your Software License Agreement for provisions   */
/*  on use, duplication, or disclosure.                       */
/*  US. Government users and prime contractors are also       */
/*  governed by the following Data Rights Notice:             */
/*  Use, duplication or disclosure by the US. Government is   */
/*  subject to the provisions set forth in FAR 52.227-14 for  */
/*  restricted rights or other agency data rights provisions  */
/*  on commercial software and documentation, as applicable.  */
/*                                                            */
/*  This file: (a) was developed at private expense, and no   */
/*  part was developed with government funds, (b) is a        */
/*  trade secret of Mercury Computer Systems, Inc. for the    */
/*  purposes of the Freedom of Information Act, (c) is        */
/*  "commercial computer software" subject to limited         */
/*  utilization as provided in the above noted Software       */
/*  License Agreement and (d) is in all respects data         */
/*  belonging exclusively to Mercury Computer Systems, Inc..  */
/*                                                            */
/**************************************************************/

/******************************************************************/
/* Copyright (c) 1999-2003, Mercury Computer Systems, Inc.,       */
/* Chelmsford MA.,and all third party embedded software sources.  */
/* All rights reserved under the Copyright laws of US. and        */
/* international treaties.                                        */
/*                                                                */
/* Patent Pending                                                 */
/******************************************************************/


//$COPYRIGHT_END


#include <SceOsThreadManager.h>
#include <SceOsSizeCheck.h>
#include <cassert>
#include <string>
#include <windows.h>
#include "SceOsWin32Error.h"

inline
HANDLE &
o2h (char * ptr)
  throw ()
{
  return *reinterpret_cast<HANDLE *> (ptr);
}

/*
 * Entry point for the new thread. This is necessary because of
 * a difference in signatures: we use a function w/o return, while
 * Win32 wants a function returning DWORD.
 *
 * Have to use heap for the RunThreadParams to avoid race conditions,
 * in case the creating thread immediately calls detach() and the
 * destructor, before the new thread gets a chance to run.
 */

namespace {
  struct RunThreadParams {
    void (*func) (void *);
    void * opaque;
  };

  DWORD WINAPI
  RunThread (void * arg)
    throw ()
  {
    RunThreadParams * p = static_cast<RunThreadParams *> (arg);
    void (*func) (void *) = p->func;
    void * opaque = p->opaque;
    delete p;
    func (opaque);
    return 0;
  }
}

SCE::OS::ThreadManager::ThreadManager ()
  throw ()
{
  assert ((compileTimeSizeCheck<sizeof (m_osOpaque), sizeof (HANDLE)> ()));
  assert (sizeof (m_osOpaque) >= sizeof (HANDLE));
#if !defined(NDEBUG)
  o2h (m_osOpaque) = 0;
#endif
}

SCE::OS::ThreadManager::ThreadManager (void (*func) (void *), void * opaque)
  throw (std::string)
{
  assert ((compileTimeSizeCheck<sizeof (m_osOpaque), sizeof (HANDLE)> ()));
  assert (sizeof (m_osOpaque) >= sizeof (HANDLE));
#if !defined(NDEBUG)
  o2h (m_osOpaque) = 0;
#endif
  start (func, opaque);
}

SCE::OS::ThreadManager::~ThreadManager ()
  throw ()
{
#if !defined(NDEBUG)
  assert (!o2h (m_osOpaque));
#endif
}

void
SCE::OS::ThreadManager::start (void (*func) (void *), void * opaque)
  throw (std::string)
{
#if !defined(NDEBUG)
  assert (!o2h (m_osOpaque));
#endif

  RunThreadParams * p = new RunThreadParams;
  p->func = func;
  p->opaque = opaque;

  o2h (m_osOpaque) = CreateThread (0, 0, RunThread, p, 0, 0);
  if (!(o2h (m_osOpaque))) {
    throw "Error starting new thread";
  }
}

void
SCE::OS::ThreadManager::join ()
  throw (std::string)
{
#if !defined(NDEBUG)
  assert (o2h (m_osOpaque));
#endif

  WaitForSingleObject (o2h (m_osOpaque), INFINITE);
  CloseHandle (o2h (m_osOpaque));

#if !defined(NDEBUG)
  o2h (m_osOpaque) = 0;
#endif
}

void
SCE::OS::ThreadManager::detach ()
  throw (std::string)
{
#if !defined(NDEBUG)
  assert (o2h (m_osOpaque));
#endif

  CloseHandle (o2h (m_osOpaque));

#if !defined(NDEBUG)
  o2h (m_osOpaque) = 0;
#endif
}
